﻿package com.moonshineproject.plugin.project.vo.flash 
{
	import __AS3__.vec.Vector;
	
	import com.moonshineproject.plugin.project.vo.ProjectVO;
	import com.moonshineproject.plugin.settings.vo.ColorSetting;
	import com.moonshineproject.plugin.settings.vo.ISetting;
	import com.moonshineproject.plugin.settings.vo.IntSetting;
	import com.moonshineproject.plugin.settings.vo.PathSetting;
	import com.moonshineproject.plugin.settings.vo.SettingsWrapper;
	
	import flash.filesystem.File;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;
	
	public class AS3ProjectVO extends ProjectVO 
	{
		public static const TEST_MOVIE_EXTERNAL_PLAYER:String = "ExternalPlayer";
		public static const TEST_MOVIE_CUSTOM:String = "Custom";
		
		public var projectFile:File;
		public var projectName:String;
		
		public var swfOutput:SWFOutputVO;
		public var buildOptions:BuildOptions;
		
		public var classpaths:Vector.<File> = new Vector.<File>();
		public var libraryPaths:Vector.<File> = new Vector.<File>();
		public var targets:Vector.<File> = new Vector.<File>();
		public var intrinsics:XMLList; // Cheating since we don't use this.
		
		public var config:MXMLCConfigVO;
		
		public var testMovie:String = TEST_MOVIE_EXTERNAL_PLAYER;
		public var testMovieCommand:String;
		
		protected var newProject:Boolean;
		protected var configInvalid:Boolean = true;
		
		public function AS3ProjectVO(root:File, projectFile:File) 
		{
			super(root);
			
			this.projectFile = projectFile;
			
			swfOutput = new SWFOutputVO();
			buildOptions = new BuildOptions();
			
			if (projectFile)
			{			
				projectName = projectFile.name.substring(0, projectFile.name.lastIndexOf("."));
				config = new MXMLCConfigVO(file.resolvePath("obj/"+projectName+"Config.xml"));
				
				var stream:FileStream = new FileStream();
				stream.open(projectFile, FileMode.READ);
				var data:XML = XML(stream.readUTFBytes(projectFile.size));
				stream.close();
				parse(data);
			}
			else
			{
				config = new MXMLCConfigVO();
				newProject = true;
			}
		}

		public function getSettings():Vector.<SettingsWrapper>
		{
			var settings:Vector.<SettingsWrapper> = new Vector.<SettingsWrapper>();
			
			var output:SettingsWrapper = new SettingsWrapper('Output', 
				Vector.<ISetting>([
					new IntSetting(swfOutput, 'frameRate', 	'Framerate (FPS)'),
					new IntSetting(swfOutput, 'width', 		'Width'),
					new IntSetting(swfOutput, 'height', 	'Height'),
					new ColorSetting(swfOutput, 'background', 'Background color'),
					new IntSetting(swfOutput, 'swfVersion', 'Minimum player version')
				])
			);
			
			settings.push(output);
			
			var buildOptions:SettingsWrapper = new SettingsWrapper('Build options',
				Vector.<ISetting>([
					new PathSetting(buildOptions, 'customSDKPath', 'Custom SDK', true, buildOptions.customSDKPath),
				])
			);
			settings.push(buildOptions);
			
			return settings;
		}
		
		override public function saveSettings():void
		{
			if (newProject)
			{
				if (createFiles() == false)
				{
					return;
				} 
				
				newProject = false;
			}
				
			var output:XML = toXML();
			
			var fw:FileStream = new FileStream();
			
			fw.open(projectFile, FileMode.WRITE);
			// Does not prefix with a 16-bit length word like writeUTF() does
			fw.writeUTFBytes('<?xml version="1.0" encoding="utf-8"?>\n' + output.toXMLString());
			fw.close();
		}
		
		// When project is created we have some scaffolding to do
		private function createFiles():Boolean
		{
			// TODO: We need to tell the user if we fail.
			if (!file || !projectName) return false;
		
			file = file.resolvePath(projectName);
			file.createDirectory();
			
			projectFile = file.resolvePath(projectName + ".as3proj");
			
			var main:File = file.resolvePath(projectName + ".as");
			if (!main.exists)
			{
				var fw:FileStream = new FileStream();
				fw.open(main, FileMode.APPEND);
				fw.writeUTFBytes("");
				fw.close();
			}
			// Main compilation target
			targets.push(main);
			
			// List all files for project
			updateChildren();
			
			return true; 
		}
		
		
		/*
			Serialize to FlashDevelop compatible XML project file.
		*/
		public function toXML():XML
		{
			var project:XML = <project></project>;
			
			// Get output node with relative paths		
			var output:XML = swfOutput.toXML(file.nativePath);
			project.appendChild(output);
			
			var classPaths:XML = <classpaths/>;
			var cpPath:String;
			for each (var cp:File in this.classpaths)
			{
				if (isFileIncludedInProject(cp))
				{
					cpPath = cp.nativePath.substring(file.nativePath.length);
				}
				else
				{
					cpPath = cp.nativePath;
				}
				var cpx:XML = new XML("<class/>");
				cpx.@path = cpPath;
				classPaths.appendChild(cpx);
			}
			project.appendChild(classPaths);
			
			project.appendChild( buildOptions.toXML() );
			
			// TODO: Output real data
			project.appendChild( new XML("<includeLibraries/>") );
			
			var libPaths:XML = <libraryPaths/>;
			var lbPath:String;
			for each (var lb:File in libraryPaths)
			{
				lbPath = lb.nativePath.substring(file.nativePath.length+1);
				var lbx:XML = <element/>;
				lbx.@path = lbPath;
				libPaths.appendChild(lbx);
			}
			project.appendChild(libPaths);
			
			// TODO: Output real data
			project.appendChild( <externalLibraryPaths/> );
			project.appendChild( <rslPaths/> );
			if (intrinsics)
			{ 
				project.appendChild(intrinsics);
			}
			else
			{
				project.appendChild( <intrinsics/> )	
			}
			project.appendChild( <library/> );
			
			var compileTargets:XML = <compileTargets/>;
			var ctPath:String;
			for each (var ct:File in targets)
			{
				ctPath = ct.nativePath.substring(file.nativePath.length+1);
				var ctx:XML = <compile/>;
				ctx.@path = ctPath;
				compileTargets.appendChild(ctx);
			}
			
			project.appendChild(compileTargets);
			
			// TODO: Output real data
			project.appendChild( <hiddenPaths/> );
			project.appendChild( <preBuildCommand /> );
			project.appendChild( <postBuildCommand alwaysRun="False" /> );
			
			var options:XML = <options/>;
			options.appendChild( <option showHiddenPaths="False" /> );
			
			var optTest:XML = <option/>;
			optTest.@testMovie = testMovie;
			options.appendChild(optTest);
			
			if (testMovie == TEST_MOVIE_CUSTOM)
			{
				var optRun:XML = <option/>;
				optRun.@testMovieCommand = testMovieCommand;
				options.appendChild(optRun);	
			}
			
			project.appendChild(options);
				
			return project;
		}
		
		private function parse(data:XML):void 
		{
			classpaths.length = 0;
			targets.length = 0;
			
			var outputData:XMLList = data.output.movie;
			swfOutput.path = file.resolvePath(outputData.@path);
			swfOutput.frameRate = Number(outputData.@fps);
			swfOutput.width = int(outputData.@width);
			swfOutput.height = int(outputData.@height);
			swfOutput.swfVersion = uint(outputData.@version);
			swfOutput.background = uint("0x"+String(outputData.@background).substr(1));
			
			for each (var classTag:XML in data.classpaths["class"]) 
			{
				var classpath:File = file.resolvePath(classTag.@path);
				classpath.canonicalize();
				if (classpath.nativePath != file.nativePath) classpaths.push(classpath);
			}
			
			buildOptions.parse(data.build);
			
			// Cheating. Just so it doesn't get lost on outputting.
			intrinsics = data.intrinsics;
			
			for each (var elementTag:XML in data.libraryPaths.element) 
			{
				libraryPaths.push(file.resolvePath(elementTag.@path));
			}
			
			for each (var compileTag:XML in data.compileTargets.compile) 
			{
				targets.push(file.resolvePath(compileTag.@path));
			}
			
			testMovie = data.options.option.@testMovie;
			if (testMovie == TEST_MOVIE_CUSTOM) 
			{
				testMovieCommand = data.options.option.@testMovieCommand;
			}
		}
		
		public function updateConfig():void 
		{
			if (configInvalid)
			{
				config.write(this);
				configInvalid = false;
			}
		}
	}
}